﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using Red.Controls.Curve.ViewModels;
using Red.Controls.TimeLine.Core;
using Red.Controls.ViewModels;

namespace Red.Controls.Curve
{
    public class CurveControl : UserControl
    {
        private CurveViewModel _model = null;

        private static Dictionary<string, SolidColorBrush> _colors;
        private static Random _rand;

        private TimeRuler _timeRuler;
        private TimeRuler _valuesRuler;

        public Action<CurveControl> OnActivation = null;
        public FrameworkElement AdornedElement { get; private set; }

        public ObservableCollection<TrackControl> Tracks { get; private set; }
        public TrackControl ActiveTrack { get; private set; }

        public static readonly DependencyProperty InterpolationTypeProperty = DependencyProperty.Register( "InterpolationType",
               typeof( EInteroplationType ), typeof( CurveControl ), new FrameworkPropertyMetadata( EInteroplationType.CPT_Linear, InterpolationTypeProperty_PropertyChanged ) );

        public static readonly DependencyProperty LockedProperty = DependencyProperty.Register( "Locked",
               typeof( bool ), typeof( CurveControl ), new FrameworkPropertyMetadata( false, LockedProperty_PropertyChanged ) );

        public EInteroplationType InterpolationType
        {
            get { return (EInteroplationType)GetValue( InterpolationTypeProperty ); }
            set { SetValue( InterpolationTypeProperty, value ); }
        }

        public bool Locked
        {
            get { return (bool)GetValue( LockedProperty ); }
            set { SetValue( LockedProperty, value ); }
        }

        static CurveControl()
        {
            _rand = new Random();

            _colors = new Dictionary<string, SolidColorBrush>();
            _colors.Add( "Red", new SolidColorBrush( Colors.Red ) );
            _colors.Add( "Green", new SolidColorBrush( Colors.Green ) );
            _colors.Add( "Blue", new SolidColorBrush( Colors.Blue ) );
            _colors.Add( "Alpha", new SolidColorBrush( Colors.White ) );
        }

        public CurveControl( FrameworkElement adornedElement, TimeRuler timeRuler, TimeRuler valuesRuler )
        {
            AdornedElement = adornedElement;
            _timeRuler = timeRuler;
            _valuesRuler = valuesRuler;

            Tracks = new ObservableCollection<TrackControl>();
            ActiveTrack = null;

            DataContextChanged += CurveControl_DataContextChanged;
        }

        public void Rearrange()
        {
            foreach ( var track in Tracks )
            {
                track.Rearrange();
            }
        }

        private void Lock( bool locked = true )
        {
            foreach ( var track in Tracks )
            {
                track.Locked = locked;
            }
        }

        private void OnInterpolationTypeChanged()
        {
            foreach ( var track in Tracks )
            {
                track.ChangeInterpolationType( InterpolationType );
            }
        }

        public void Show()
        {
            foreach ( var track in Tracks )
            {
                AdornerLayer.GetAdornerLayer( AdornedElement ).Add( track );
            }
        }
        public void Hide()
        {
            foreach ( var track in Tracks )
            {
                AdornerLayer.GetAdornerLayer( AdornedElement ).Remove( track );
            }
        }

        public void Activate( TrackControl tc )
        {
            ActiveTrack = tc;

            if ( OnActivation != null )
            {
                OnActivation( this );
            }
        }

        static void LockedProperty_PropertyChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e )
        {
            CurveControl cc = obj as CurveControl;
            if ( cc != null )
            {
                cc.Lock( (bool)e.NewValue );
            }
        }
        static void InterpolationTypeProperty_PropertyChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e )
        {
            CurveControl cc = obj as CurveControl;
            if ( cc != null )
            {
                cc.OnInterpolationTypeChanged();
            }
        }

        public void AddPoint( Point p )
        {
            if ( Locked )
            {
                return;
            }

            double time = _timeRuler.StartValue + p.X / _timeRuler.PixelToValueRatio;
            double value = _valuesRuler.StartValue + ( AdornedElement.RenderSize.Height - p.Y ) / _valuesRuler.PixelToValueRatio;

            foreach ( var track in Tracks )
            {
                track.AddPoint( p );
            }

            //HandleDataChange();
        }

        public void RemovePoint( double time )
        {
            foreach ( var track in Tracks )
            {
                track.RemovePoint( time );
            }

            //HandleDataChange();
        }

        public void OnPointMoved( int index, double newTime )
        {
            foreach ( var track in Tracks )
            {
                track.OnCurvePointMovementFinished( index, newTime );
            }
        }

        public void Save()
        {
            var dc = DataContext as DataItem;
            if ( dc != null && _model != null )
            {
                dc.Value = CurveViewModelPacker.PackToString( _model );
            }
        }

        public void RevertChanges()
        {
            var dc = DataContext as DataItem;
            if ( dc != null && _model != null )
            {
                dc.Value = _model.CachedDataString;
                Rebuild();
            }
        }

        private void CurveControl_DataContextChanged( object sender, DependencyPropertyChangedEventArgs e )
        {
            var oldDc = e.OldValue as DataItem;
            if ( oldDc != null )
            {
                oldDc.PropertyChanged -= CurveItem_PropertyChanged;
            }

            var dc = e.NewValue as DataItem;
            if ( dc != null )
            {
                Rebuild();
                dc.PropertyChanged += CurveItem_PropertyChanged;
            }
        }

        private void CurveItem_PropertyChanged( object sender, System.ComponentModel.PropertyChangedEventArgs e )
        {
            if ( e.PropertyName == "Value" )
            {
                Rebuild();
            }
        }

        private void Rebuild()
        {
            var dc = DataContext as DataItem;
            if ( dc != null )
            {
                _model = CurveViewModelBuilder.CreateFrom( dc.Value );

                Tracks.Clear();
                foreach ( var track in _model.Tracks )
                {
                    Tracks.Add( new TrackControl( this, _colors.ContainsKey( track.Name ) ? _colors[track.Name] : new SolidColorBrush(
                                                   new Color() { R = (byte)_rand.Next( 0, 255 ), G = (byte)_rand.Next( 0, 255 ), B = (byte)_rand.Next( 0, 255 ), A = 255 } ),
                                                   _timeRuler, _valuesRuler ) );
                    Tracks.Last().DataContext = track;
                }
            }
        }
    }
}
